/**
 * Copyright (c) 2016-2019 bootwaf开源 All rights reserved.
 *
 * http://www.ihuanzhi.com
 *
 * 版权所有，侵权必究！
 */

package com.bwf.common.utils.scan;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.bwf.common.annotation.ApiExplain;
import com.bwf.modules.sys.dao.SysPathDao;
import com.bwf.modules.sys.entity.SysPathEntity;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

public class ScanPathUtils {
	private static final String rootUrl = "";

	protected static Logger logger = LoggerFactory.getLogger(ScanPathUtils.class);
	
	/**
	 * 循环文件夹下文件及文件夹列表
	 * @param files
	 * @param absolutePath 根文件路径
	 * @throws ClassNotFoundException
	 */
	public static void fileIterator(SysPathDao sysPathDao,File[] files,String absolutePath) throws ClassNotFoundException {
		/**
		 * 循环文件
		 */
		for (File f : files) {
			//当前对象是文件夹
			if(f.isDirectory()){
				File[] files2 = f.listFiles();//文件夹下列表
				/**
				 * 递归调用
				 */
				fileIterator(sysPathDao,files2,absolutePath);
				continue;
			}
			/**
			 * 获取类名
			 */
			String className = f.getName().substring(0,
					f.getName().indexOf(".class"));
			/**
			 * 只解析Controller
			 */
			if(className.endsWith("Controller")) {
				/**
				 * 判断文件路径，调整为类报名结构
				 */
				String path=f.getAbsolutePath().replace(absolutePath, "").replace(f.getName(), "").replace(File.separator, ".");
				/**
				 * 获取类
				 */
				Class clazz = Class.forName("com.bwf."+(path.startsWith(".")?path.substring(1):path)+ className);
				/**
				 * 解析类
				 */
				parseClass(sysPathDao,clazz); // 解析 出url
			}
		}
	}

	/**
	 * 类解析
	 * @param clazz 所解析的类
	 */
	public static void parseClass(SysPathDao sysPathDao,Class clazz) {
		/**
		 * 判断类是否包含Controller/RestController注解，是否是控制器
		 */
		if (clazz.isAnnotationPresent(Controller.class)||clazz.isAnnotationPresent(RestController.class)) { // 是否为一个controller ?或者RestController
		
			/**
			 * 路径对象
			 */
			SysPathEntity pathEntity=new SysPathEntity();
			
			logger.info("所在的类："+clazz.getName());
			pathEntity.setClazz(clazz.getName());
			
			
			
			/**
			 * 设置PathEntity的ClassExplain
			 */
			setClassExplain(pathEntity,clazz);
			/**
			 * 类上面的路径注解
			 */
			String classUrl = "";
			/**
			 * 获取类的映射路径[多路径只取第一个]
			 */
			classUrl = getClassUrl(clazz);
			
			/**
			 * 获取类的所有方法
			 */
			Method[] ms = clazz.getDeclaredMethods();
			
			/**
			 * 循环方法集合
			 */
			for (Method m : ms) {
				pathEntity.setMethod(m.getName());
				logger.info("方法名："+m.getName());
				
				logger.info("方法注解是否包含Mapping："+(m.isAnnotationPresent(RequestMapping.class)
						||m.isAnnotationPresent(PostMapping.class)
						||m.isAnnotationPresent(GetMapping.class)
						||m.isAnnotationPresent(PutMapping.class)
						||m.isAnnotationPresent(DeleteMapping.class)));
				/**
				 * 判断方法上是否包含请求路径的注解
				 */
				if (m.isAnnotationPresent(RequestMapping.class)
						||m.isAnnotationPresent(PostMapping.class)
						||m.isAnnotationPresent(GetMapping.class)
						||m.isAnnotationPresent(PutMapping.class)
						||m.isAnnotationPresent(DeleteMapping.class)) {
					
					/**
					 * 方法说明
					 */
					if (m.isAnnotationPresent(ApiExplain.class)) {
						ApiExplain webExplain_method = m.getAnnotation(ApiExplain.class);
						/**
						 * 获取方法路径
						 */
						String methodExplain = webExplain_method.value();
						/**
						 * 开发者
						 */
						String author = webExplain_method.author();
						/**
						 * 方法说明
						 */
						logger.info("\t自定义注解获取方法说明："+methodExplain +"\t 开发者："+author);
						pathEntity.setMethodExplain(methodExplain);
						pathEntity.setAuthor(author);
						
					}
					
					/**
					 * 判断注解类型,解析注解
					 */
					if(m.isAnnotationPresent(RequestMapping.class)) {
						logger.info("方法为：requestMapping");
						RequestMapping requestMapping_method = m.getAnnotation(RequestMapping.class);
						getMethodInfo(pathEntity, classUrl, m, requestMapping_method);
					}else if(m.isAnnotationPresent(PostMapping.class)) {
						logger.info("方法为：PostMapping");
						PostMapping postMapping_method = m.getAnnotation(PostMapping.class);
						//转换为requestMapping
						getMethodInfo(pathEntity, classUrl, m, postMapping_method);
					}else if(m.isAnnotationPresent(GetMapping.class)) {
						logger.info("方法为：GetMapping");
						GetMapping getMapping_method = m.getAnnotation(GetMapping.class);
						//转换为requestMapping
						getMethodInfo(pathEntity, classUrl, m, getMapping_method);
					}else if(m.isAnnotationPresent(PutMapping.class)) {
						logger.info("方法为：PutMapping");
						PutMapping putMapping_method = m.getAnnotation(PutMapping.class);
						//转换为requestMapping
						getMethodInfo(pathEntity, classUrl, m, putMapping_method);
					}else if(m.isAnnotationPresent(DeleteMapping.class)) {
						logger.info("方法为：DeleteMapping");
						DeleteMapping deleteMapping_method = m.getAnnotation(DeleteMapping.class);
						//转换为requestMapping
						getMethodInfo(pathEntity, classUrl, m, deleteMapping_method);
					}
						
						

					/**
					 * 判断是否包含权限注解
					 */
					if (m.isAnnotationPresent(RequiresPermissions.class)) {
						/**
						 * 方法上的权限注解
						 */
						RequiresPermissions requiresPermissions_method = m
								.getAnnotation(RequiresPermissions.class);
						/**
						 * 获取权限注解
						 */
						String[] value = requiresPermissions_method.value();
						if(value!=null&&value.length>0){
							String permissions="";
							for (int i = 0; i < value.length; i++) {
								permissions+=value[i]+",";
							}
							permissions=permissions.substring(0, permissions.length()-1);
							logger.info("\t权限："+permissions);
							pathEntity.setPermissions(permissions);
						}
						
					}
					
					/**
					 * 判断是否包含用户限制
					 */
					if (m.isAnnotationPresent(RequiresUser.class)) {
						/**
						/**
						 * 获取是否登录
						 */
						logger.info("\t限制用户：登录才能访问");
						pathEntity.setForUsers("限制登录才能访问");
					}
					
					/**
					 * 判断是否包含角色限制
					 */
					if (m.isAnnotationPresent(RequiresRoles.class)) {
						/**
						 * 方法上的权限注解
						 */
						RequiresRoles requiresRoles_method = m
								.getAnnotation(RequiresRoles.class);
						/**
						 * 获取限制角色注解
						 */
						String[] value = requiresRoles_method.value();
						if(value!=null&&value.length>0){
							String roles="";
							for (int i = 0; i < value.length; i++) {
								roles+=" ["+value[i]+"] ";
							}
							logger.info("\t限制角色："+roles);
							pathEntity.setForRoles(roles);
						}
						
					}
					
					logger.info("\t\n");
					pathEntity.setUpdateDate(new Date());
					pathEntity.setCreateDate(pathEntity.getUpdateDate());
					sysPathDao.insert(pathEntity);
				}
				
			}
		}
	}

	private static void getMethodInfo(SysPathEntity pathEntity, String classUrl, Method m,DeleteMapping deleteMapping_method) {
		String methodUrl="";
		methodUrl = deleteMapping_method.value()[0];
		
		setMethodUrl(pathEntity, classUrl, methodUrl);
		/**
		 * 获取请求方式
		 */
		String requestMethod="DELETE";
		logger.info("\t请求方式："+requestMethod);
		/**
		 * 设置请求方式
		 */
		setRequestMethod(pathEntity, requestMethod);
		/**
		 * 方法说明
		 */
		logger.info("\trequestMapping_method获取方法说明："+deleteMapping_method.name());
		
		/**
		 * 根据APIOperation设置MethodExplation
		 */
		setMethodExplain(pathEntity,deleteMapping_method.name(), m);
		
	}
	


	private static void getMethodInfo(SysPathEntity pathEntity, String classUrl, Method m,
			PutMapping putMapping_method) {
		String methodUrl="";
		methodUrl = putMapping_method.value()[0];
		
		setMethodUrl(pathEntity, classUrl, methodUrl);
		/**
		 * 获取请求方式
		 */
		String requestMethod="PUT";
		logger.info("\t请求方式："+requestMethod);
		setRequestMethod(pathEntity, requestMethod);
		/**
		 * 方法说明
		 */
		logger.info("\trequestMapping_method获取方法说明："+putMapping_method.name());
		/**
		 * 根据APIOperation设置MethodExplation
		 */
		setMethodExplain(pathEntity,putMapping_method.name(), m);
		
	}

	private static void getMethodInfo(SysPathEntity pathEntity, String classUrl, Method m,
			GetMapping getMapping_method) {
		String methodUrl="";
		methodUrl = getMapping_method.value()[0];
		
		setMethodUrl(pathEntity, classUrl, methodUrl);
		/**
		 * 获取请求方式
		 */
		String requestMethod="GET";
		logger.info("\t请求方式："+requestMethod);
		setRequestMethod(pathEntity, requestMethod);
		/**
		 * 方法说明
		 */
		logger.info("\trequestMapping_method获取方法说明："+getMapping_method.name());
		/**
		 * 根据APIOperation设置MethodExplation
		 */
		setMethodExplain(pathEntity,getMapping_method.name(), m);
		
	}

	private static void getMethodInfo(SysPathEntity pathEntity, String classUrl, Method m,
			PostMapping postMapping_method) {
		String methodUrl="";
		methodUrl = postMapping_method.value()[0];
		
		setMethodUrl(pathEntity, classUrl, methodUrl);
		/**
		 * 获取请求方式
		 */
		String requestMethod="POST";
		logger.info("\t请求方式："+requestMethod);
		setRequestMethod(pathEntity, requestMethod);
		/**
		 * 方法说明
		 */
		logger.info("\trequestMapping_method获取方法说明："+postMapping_method.name());
		setMethodExplain(pathEntity,postMapping_method.name(), m);
		
	}

	/**
	 * 获取方法的相关
	 * @param pathEntity
	 * @param classUrl
	 * @param m
	 * @param mappingType
	 */
	private static void getMethodInfo(SysPathEntity pathEntity, String classUrl, Method m, RequestMapping requestMapping_method) {
		String methodUrl="";
		methodUrl = requestMapping_method.value()[0];
		
		setMethodUrl(pathEntity, classUrl, methodUrl);
		/**
		 * 获取请求方式
		 */
		String requestMethod=findRequestMethod(requestMapping_method.method());
		logger.info("获取请求方式："+requestMethod);
		requestMethod="".equals(requestMethod)?"不限":requestMethod;
		logger.info("\t请求方式："+requestMethod);
		setRequestMethod(pathEntity, requestMethod);
		/**
		 * 方法说明
		 */
		logger.info("\trequestMapping_method获取方法说明："+requestMapping_method.name());
		setMethodExplain(pathEntity,requestMapping_method.name(), m);
	}

	/**
	 * 获取类的映射路径
	 * @param clazz
	 * @param classUrl
	 * @return
	 */
	private static String getClassUrl(Class clazz) {
		
		String classUrl="";
		/**
		 * 获取类的RequestMapping注解，映射请求路径地址
		 */
		if (clazz.isAnnotationPresent(RequestMapping.class)) {
			RequestMapping requestMapping_clazz = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
			/**
			 * 获取第一个路径映射地址
			 */
			classUrl = requestMapping_clazz.value()[0];
			
			/**
			 * 去除类路径的最后一个/
			 */
			if (classUrl.equals("/")) { // 如果是"/" 的话 制空
				classUrl = "";
			}
		}
		return classUrl;
	}

	/**
	 * 设置classExplain
	 * @param pathEntity 设置对象
	 * @param clazz 依据类
	 */
	private static void setClassExplain(SysPathEntity pathEntity,Class clazz) {
		/**
		 * 注释说明
		 */
		if (clazz.isAnnotationPresent(ApiExplain.class)) {
			ApiExplain webExplain_clazz = (ApiExplain)clazz.getAnnotation(ApiExplain.class);
			/**
			 * 获取方法路径
			 */
			String clazzExplain = webExplain_clazz.value();
			/**
			 * 类说明
			 */
			logger.info("\t根据自定义注解类说明："+clazzExplain);
			pathEntity.setClazzExplain(clazzExplain);
		}
		/**
		 * 注释说明
		 */
		if (clazz.isAnnotationPresent(Api.class)) {
			Api api = (Api)clazz.getAnnotation(Api.class);
			/**
			 * 获取方法路径
			 */
			String[] tags = api.tags();
			/**
			 * 类说明
			 */
			logger.info("\t根据API注解类说明："+Arrays.toString(tags));
			pathEntity.setClazzExplain(Arrays.toString(tags));
		}
	}
	
	/**
	 * 获取请求方式
	 * @param method
	 * @return
	 */
	private static String findRequestMethod(RequestMethod ... methods) {
		StringBuilder sb = new StringBuilder();
		if(methods.length>0){
			return Arrays.toString(methods);
		}
		return sb.toString();
	}
	
	/**
	 * 根据APIOperation设置MethodExplation
	 * @param pathEntity
	 * @param m
	 */
	private static void setMethodExplain(SysPathEntity pathEntity,String methodExplain, Method m) {
		/**
		 * 根据传进来的参数设置方法说明，如果有api注解则重新设置
		 */
		pathEntity.setMethodExplain(methodExplain);
		/**
		 * swagger注解信息
		 */
		if(m.isAnnotationPresent(ApiOperation.class)) {
			ApiOperation apiOperation=m.getAnnotation(ApiOperation.class);
			/**
			 * 方法说明
			 */
			logger.info("\tApiOperation获取方法说明："+apiOperation.value());
			pathEntity.setMethodExplain(apiOperation.value());
		}
	}

	/**
	 * 设置方法路径
	 * @param pathEntity
	 * @param classUrl
	 * @param methodUrl
	 */
	private static void setMethodUrl(SysPathEntity pathEntity, String classUrl, String methodUrl) {
		/**
		 * 获取 完整请求路径
		 */
		String resultUrl = rootUrl + classUrl + (methodUrl.startsWith("/")?methodUrl:"/"+methodUrl);
		/**
		 * 替换数据路径中的表达式
		 */
		resultUrl=resultUrl.replace("//", "/");
		logger.info("\t路径："+resultUrl);
		pathEntity.setPathUrl(resultUrl);
	}
	
	/**
	 * 设置请求方式
	 * @param pathEntity
	 * @param requestMethod
	 */
	private static void setRequestMethod(SysPathEntity pathEntity, String requestMethod) {
		/**
		 * 设置请求方式保存
		 */
		pathEntity.setRequestMethod(requestMethod);
		//设置响应方式
		pathEntity.setResType("");
	}
}
